#include "SHA1Digest.h"

uint32_t SHA1Digest::H_INIT[SHA1_RESULT_UINT32] =
{
	0x67452301,
	0xEFCDAB89,
	0x98BADCFE,
	0x10325476,
	0xC3D2E1F0
};

uint32_t SHA1Digest::K[SHA1_ROUND_CNT] =
{
	0x5A827999,
	0x6ED9EBA1,
	0x8F1BBCDC,
	0xCA62C1D6
};

std::function<uint32_t(uint32_t, uint32_t, uint32_t)> SHA1Digest::F[SHA1_ROUND_CNT] =
{
	[](uint32_t x, uint32_t y, uint32_t z) -> uint32_t
	{
		return (x & y) | (~x & z);
	},
	[](uint32_t x, uint32_t y, uint32_t z) -> uint32_t
	{
		return x ^ y ^ z;
	},
	[](uint32_t x, uint32_t y, uint32_t z) -> uint32_t
	{
		return (x & y) | (x & z) | (y & z);
	},
	[](uint32_t x, uint32_t y, uint32_t z) -> uint32_t
	{
		return x ^ y ^ z;
	},
};

uint32_t SHA1Digest::S(uint32_t x, int n)
{
	return (x << n) | (x >> (32 - n));
}

uint32_t SHA1Digest::Expand(uint32_t W[], int i)
{
	return S((W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]), 1);
}

void SHA1Digest::Round(uint32_t alpha[], uint32_t W[], int i)
{
	uint32_t& A = alpha[0];
	uint32_t& B = alpha[1];
	uint32_t& C = alpha[2];
	uint32_t& D = alpha[3];
	uint32_t& E = alpha[4];

	uint32_t tmp = S(A, 5) + F[i / SHA1_ROUND_LEN](B, C, D) + E + W[i] + K[i / SHA1_ROUND_LEN];
	E = D;
	D = C;
	C = S(B, 30);
	B = A;
	A = tmp;
}

std::string SHA1Digest::BytesToHex(const unsigned char* input, size_t length, bool isUpcase)
{
	static const char* digitUpper = "0123456789ABCDEF";
	static const char* digitLower = "0123456789abcdef";

	const char* pstr = isUpcase ? digitUpper : digitLower;

	std::string ret;
	ret.reserve(length << 1);
	for (unsigned int i = 0; i < length; ++i)
	{
		ret.push_back(pstr[(input[i] & 0xF0) >> 4]);
		ret.push_back(pstr[input[i] & 0x0F]);
	}
	return ret;
}

void SHA1Digest::BytesReverse(uint32_t data[], size_t length)
{
	static unsigned int bitsOfByte = 8;

	for (unsigned int i = 0; i < length; ++i)
	{
		data[i] = ((data[i] >> (0 * bitsOfByte) & 0xFF) << (3 * bitsOfByte)) |
			((data[i] >> (1 * bitsOfByte) & 0xFF) << (2 * bitsOfByte)) |
			((data[i] >> (2 * bitsOfByte) & 0xFF) << (1 * bitsOfByte)) |
			((data[i] >> (3 * bitsOfByte) & 0xFF) << (0 * bitsOfByte));
	}
}
